#include "plateau.h"
#include <iostream>
#include <fstream>
#include <limits>
#include <stdlib.h>
#include <time.h>

using namespace std ;



/**
  * @brief Constructeur du plateau de jeu
  * @param largeur La largeur du plateau
  * @param hauteur La hauteur du plateau
  * @param load True si une partie a été chargée, false sinon
  */
Plateau::Plateau(int largeur, int hauteur, bool load)
{
    _largeur = largeur ;
    _hauteur = hauteur ;
    _derniereColonneJouee = -1 ;
    init(load) ;
}



/**
  * @brief Initialisation du plateau de jeu avec la bonne taille et un espace dans chaque case
  * @param load True si une partie a été chargée, false sinon
  */
void Plateau::init(bool load)
{
    fstream file("save", ios::in) ;
    string str ;
    if ( load && file)
    {
        getline(file,str) ;
    }
    _plateau.resize(_largeur);
    for ( unsigned int i = 0 ; i < _largeur ; i++ ) _plateau[i].resize(_hauteur) ;
    for ( unsigned int i = 0 ; i < _hauteur ; i++ )
    {
        if ( load && file)
        {
            getline(file,str) ;
        }
        for ( unsigned int j = 0 ; j < _largeur ; j++ )
        {
            if ( load && file)
            {
                _plateau[j][i] = str.at(j) ;
            }
            else _plateau[j][i] = ' ' ;
        }
    }
    if ( !file && load )
    {
        cout << "Le fichier n'a pas pu etre ouvert, la partie n'a pas ete chargee' ..." << endl ;
    }
    file.close() ;
}



/**
  * @brief Accésseur de la largeur du plateau de jeu
  * @return La largeur du plateau de jeu
  */
int Plateau::getLargeur()
{
    return _largeur ;
}



/**
  * @brief Accésseur de la hauteur du plateau de jeu
  * @return La hauteur du plateau de jeu
  */
int Plateau::getHauteur()
{
    return _hauteur ;
}



/**
  * @brief Affichage du plateau de jeu
  */
void Plateau::affichage()
{
    cout << " " ;
    for ( unsigned int j = 0 ; j < _largeur ; j++ )
    {
        if ( j < 10 ) cout << " " ;
        cout << j+1 << "  " ;
    }
    cout << endl ;
    cout << endl ;
    for ( unsigned int i = 0 ; i < _hauteur ; i++ )
    {
        for ( unsigned int j = 0 ; j < _largeur ; j++ )
        {
            cout << "| " << _plateau[j][i] << " " ;
        }
        cout << "| " << i+1 << endl << " " ;
        for ( unsigned int j = 0 ; j < _largeur*4-1 ; j++ )
        {
            cout << "-" ;
        }
        cout << endl ;
    }
    cout << " " ;
    for ( unsigned int j = 0 ; j < _largeur ; j++ )
    {
        if ( j < 10 ) cout << " " ; // Pour éviter un décalage avec une taille plus grande que 10
        cout << j+1 << "  " ;
    }
    cout << endl ;
}



/**
  * @brief Remise à zéro du plateau de jeu en remplissant toutes les cases avec un espace
  */
void  Plateau::reinitialiser()
{
    for ( unsigned int i = 0 ; i < _largeur ; i++ )
    {
        for ( unsigned int j = 0 ; j < _hauteur ; j++ )
        {
            _plateau[i][j] = ' ' ;
        }
    }
    _derniereColonneJouee = -1 ;
}



/**
  * @brief Application de la gravité sur tout le plateau de jeu
  */
void Plateau::gravite()
{
    for ( unsigned int i = 0 ; i < _largeur ; i++ )
    {
        appliquerGravite(i) ;
    }
}



/**
  * @brief Application de la gravité sur une colonne
  * @param colonne La colonne sur laquelle est appliquée la gravité
  */
void Plateau::appliquerGravite(int colonne)
{
    for ( unsigned int i = 0 ; i < _hauteur-1 ; i++ )
    {
        if ( _plateau[colonne][i] != ' ' && _plateau[colonne][i+1] == ' ' )
        {
            _plateau[colonne][i+1] = _plateau[colonne][i] ;
            _plateau[colonne][i] = ' ' ;
            appliquerGravite(colonne) ;
        }
    }
}



/**
  * @brief Permet de savoir si il est possible de jouer dans une colonne
  * @param colonne La colonne à traiter
  * @return True si la colonne n'est pas pleine, false sinon
  */
bool Plateau::jouable(int colonne)
{
    if ( _plateau[colonne][0] == ' ' ) return true ;
    return false ;
}



/**
  * @brief Permet au joueur actif de placer un pion dans une des cases possibles du plateau de jeu
  * @param joueur Le numéro du joueur actif
  * @param nom Le nom du joueur actif
  * @return True si le joueur a joué, false si il a annulé pour ne pas qu'il perde la main
  */
bool Plateau::jouer(int joueur, string nom)
{
    unsigned int colonne ;
    do
    {
        cout << nom << " : Choisissez une colonne ( Vous pouvez annuler en entrant '0' ) : " ;
        cin >> colonne ;
        cin.clear() ;
        cin.ignore(numeric_limits<int>::max(), '\n');
        cout << endl ;
        if ( colonne == 0 ) return false ;
        if ( colonne < 1 || colonne > _largeur )
        {
            cout << "Cette colonne ne fait pas partie du plateau de jeu." << endl ;
        }
        else if ( !jouable(colonne-1) )
        {
            cout << "Cette colonne est pleine, choisissez en une autre." << endl ;
            colonne = 0 ;
        }
    } while ( colonne < 1 || colonne > _largeur ) ;
    if ( joueur == 1 ) _plateau[colonne-1][0] = 'X' ;
    else _plateau[colonne-1][0] = 'O' ;
    appliquerGravite(colonne-1) ;
    _derniereColonneJouee = colonne-1 ;
    if ( estPlein() )
    {
        cout << "Le plateau de jeu est rempli sans qu'il n'y ait de gagnant, reinitialisation ..." << endl ;
        reinitialiser() ;
    }
    return true ;
}



/**
  * @brief Permet de jouer un pion dans une colonne, cette fonction est utilisée par l'ia pour faire ses tests et pour jouer
  * @param colonne La colonne dans laquelle jouer
  * @param pion Le pion à jouer
  */
void Plateau::jouer(int colonne, char pion)
{
    _plateau[colonne][0] = pion ;
    appliquerGravite(colonne) ;
}



/**
  * @brief Permet de savoir si il est possible de supprimer le contenu d'une case
  * @param colonne La colonne de la case à traiter
  * @param ligne La ligne de la case à traiter
  * @return True si la suppression est autorisée, false sinon
  */
bool Plateau::supprimable(int colonne, int ligne)
{
    if ( !casePleine(colonne,ligne) ) return false ;
    if ( colonne == _derniereColonneJouee && ligne == pionLePlusHaut(colonne) ) return false ; // Interdiction de supprimer le pion le plus haut de la dernière colonne jouée
    char pion = _plateau[colonne][ligne] ;
    if ( colonne == _derniereColonneJouee ) // Interdiction de supprimer un pion dans la dernière colonne jouer si il est identique à tous ceux situés au dessus.
    {
        while ( ligne >= 0 )
        {
         if ( _plateau[colonne][ligne] == ' ' ) return false ;
         else if ( _plateau[colonne][ligne] != pion ) return true ;
         ligne -- ;
        }
    }
    return true ;
}



/**
  * @brief Permet au joueur actif de supprimer un pion dans une des cases possibles du plateau de jeu
  * @param nom Le nom du joueur actif
  * @return True si le joueur a supprimé un pion, false si il a annulé pour ne pas qu'il perde la main
  */
bool Plateau::supprimer(string nom)
{
    unsigned int colonne, ligne ;
    bool verif = false ;
    do
    {
        do
        {
            cout << nom << " : Choisissez une colonne ( Vous pouvez annuler en entrant '0' ) : " ;
            cin >> colonne ;
            cin.clear() ;
            cin.ignore(numeric_limits<int>::max(), '\n');
            cout << endl ;
            if ( colonne == 0 ) return false ;
            if ( colonne < 1 || colonne > _largeur )
            {
                cout << "Cette colonne ne fait pas partie du plateau de jeu." << endl ;
                affichage() ;
            }
            cout << nom << " : Choisissez une ligne ( Vous pouvez annuler en entrant '0' ) : " ;
            cin >> ligne ;
            cin.clear() ;
            cin.ignore(numeric_limits<int>::max(), '\n');
            cout << endl ;
            if ( ligne == 0 ) return false ;
            if ( ligne < 1 || ligne > _hauteur )
            {
                cout << "Cette ligne ne fait pas partie du plateau de jeu." << endl ;
                affichage() ;
            }
        } while ( ( colonne < 1 || colonne > _largeur ) || ( ligne < 1 || ligne > _hauteur ) ) ;
        if ( !supprimable(colonne-1,ligne-1) )
        {
            cout << "Cette suppression est interdite..." << endl ;
            colonne = 0 ;
            ligne = 0 ;
            affichage() ;
        }
        else
        {
            verif = true ;
            _plateau[colonne-1][ligne-1] = ' ' ;
        }
    } while ( !verif ) ;
    appliquerGravite(colonne-1) ;
    _derniereColonneJouee = -1 ;
    return true ;
}



/**
  * @brief Permet de supprimer le pion le plus haut dans une colonne, cette fonction est utilisée par l'ia pour déjouer ses coups
  * @param colonne La colonne de la case à traiter
  */
void Plateau::supprimer(int colonne)
{
    int i = 0 ;
    while ( _plateau[colonne][i] == ' ' ) i++ ;
    _plateau[colonne][i] = ' ' ;
}



/**
  * @brief Permet de supprimer un pion à une colonne et une ligne, cette fonction est utilisée par l'ia pour faire ses tests et pour jouer
  * @param colonne La colonne de la case à traiter
  * @param ligne La ligne de la case à traiter
  */
void Plateau::supprimer(int colonne, int ligne)
{
    _plateau[colonne][ligne] = ' ' ;
    gravite() ;
}



/**
  * @brief Permet au joueur actif de faire tourner le plateau de jeu
  * @param nom Le nom du joueur actif
  * @return True si le plateau a été tourné, false si le joueur a annulé le coup pour qu'il ne perde pas la main
  */
bool Plateau::tourner(string nom)
{
    char sens ;
    do
    {
        cout << nom << " : Choisissez un sens pour tourner le plateau." << endl << "+ pour le sens horaire, - pour le sens antihoraire ( '0' pour annuler ) : " ;
        cin >> sens ;
        cout << endl ;
        if ( sens == '0' ) return false ;
        if ( sens != '+' && sens != '-' ) cout << "Le sens doit etre + ou -." << endl ;
        else if ( sens == '+' ) tournerDroite() ;
        else tournerGauche() ;
    } while ( sens != '+' && sens != '-' ) ;
    gravite();
    return true ;
}



/**
  * @brief Permet de faire tourner la plateau de jeu dans le sens horaire
  * @param ia True si c'est l'ia qui a demandé la rotation, false sinon
  */
void Plateau::tournerDroite(bool ia)
{
    std::vector< std::vector< char > > plateauTourne ;
    plateauTourne.resize(_hauteur);
    for ( unsigned int i = 0 ; i < _hauteur ; i++ )
    {
        plateauTourne[i].resize(_largeur) ;
        for ( unsigned int j = 0 ; j < _largeur ; j++ )
        {
            plateauTourne[i][j] = ' ' ;
        }
    }
    for ( unsigned int i = 0 ; i < _largeur ; i++ )
    {
        for ( unsigned int j = 0 ; j < _hauteur ; j++ )
        {
            plateauTourne[_hauteur-1-j][i] = _plateau[i][j] ;
        }
    }
    _plateau = plateauTourne ;
    int hauteurTmp = _hauteur ;
    _hauteur = _largeur ;
    _largeur = hauteurTmp ;
    if ( ia ) gravite() ; // La gravité est activée automatiquement à la fin de la rotation si un joueur l'a lancé
    else _derniereColonneJouee = -1 ;
}



/**
  * @brief Permet de faire tourner la plateau de jeu dans le sens anti-horaire
  * @param ia True si c'est l'ia qui a demandé la rotation, false sinon
  */
void Plateau::tournerGauche(bool ia)
{
    vector<vector<char> > plateauTourne ;
    plateauTourne.resize(_hauteur);
    for ( unsigned int i = 0 ; i < _hauteur ; i++ )
    {
        plateauTourne[i].resize(_largeur) ;
        for ( unsigned int j = 0 ; j < _largeur ; j++ )
        {
            plateauTourne[i][j] = ' ' ;
        }
    }
    for ( unsigned int i = 0 ; i < _largeur ; i++ )
    {
        for ( unsigned int j = 0 ; j < _hauteur ; j++ )
        {
            plateauTourne[j][_largeur-1-i] = _plateau[i][j] ;
        }
    }
    _plateau = plateauTourne ;
    int hauteurTmp = _hauteur ;
    _hauteur = _largeur ;
    _largeur = hauteurTmp ;
    if ( ia ) gravite() ; // La gravité est activée automatiquement à la fin de la rotation si un joueur l'a lancé
    else _derniereColonneJouee = -1 ;
}



/**
  * @brief Vérification de la présence ou non d'un alignement suffisant pour gagner
  * @param alignement Le nombre de pions à aligner pour gagner
  * @return 'X' si le joueur 1 a le bon nombre d'alignement, 'O' si c'est le joueur 2, ' ' sinon
  */
char Plateau::verif(int alignement)
{
    int alignementMaxJoueur1 = 0 ;
    int alignementMaxJoueur2 = 0 ;
    int nbAlignementsJoueur1 = verif(alignement, alignementMaxJoueur1, 'X') ;
    int nbAlignementsJoueur2 = verif(alignement, alignementMaxJoueur2, 'O') ;
    if ( nbAlignementsJoueur1 != 0 && nbAlignementsJoueur1 == nbAlignementsJoueur2 )
    {
        if ( alignementMaxJoueur1 > alignementMaxJoueur2 ) return 'X' ;
        else if ( alignementMaxJoueur2 > alignementMaxJoueur1 ) return 'O' ;
        else return ' ' ;
    }
    else if ( nbAlignementsJoueur1 > nbAlignementsJoueur2 ) return 'X' ;
    else if ( nbAlignementsJoueur1 < nbAlignementsJoueur2 ) return 'O' ;
    else return ' ' ;
}



/**
  * @brief Vérification de la victoire ou non d'un certain joueur
  * @param alignement Le nombre de pions à aligner pour gagner
  * @param alignementMax Le nombre de pions alignés par le joueur au maximum
  * @param pion Le pion à vérifier, 'X' si c'est le joueur 1, 'O' sinon
  * @return Le nombre de fois que le pion est aligné le nombre de fois indiqué par alignement
  */
int Plateau::verif(int alignement, int & alignementMax, char pion)
{
    /// Vérification sur les lignes ///

    int nbAlignements = 0 ;
    for ( unsigned int i = 0 ; i < _hauteur ; i ++ )
    {
        int compteur = 0 ;
        for ( unsigned int j = 0 ; j < _largeur ; j++ )
        {
            if ( _plateau[j][i] == pion )
            {
                compteur ++ ;
                if ( compteur > alignementMax ) alignementMax = compteur ;
            }
            else compteur = 0 ;
            if ( compteur == alignement )
            {
                int alignementMaxTemp = alignement ;
                for ( unsigned int k = j+1 ; k < _largeur ; k ++)
                {
                    if ( _plateau[k][i] == pion ) alignementMaxTemp++ ;
                    else k = _largeur ;
                }
                if ( alignementMaxTemp > alignementMax ) alignementMax = alignementMaxTemp ;
                nbAlignements ++ ;
                compteur = 0 ;
            }
        }
    }

    /// Vérification sur les colonnes ///

    for ( unsigned int i = 0 ; i < _largeur ; i ++ )
    {
        int compteur = 0 ;
        for ( unsigned int j = 0 ; j < _hauteur ; j++ )
        {
            if ( _plateau[i][j] == pion )
            {
                compteur ++ ;
                if ( compteur > alignementMax ) alignementMax = compteur ;
            }
            else compteur = 0 ;
            if ( compteur == alignement )
            {
                int alignementMaxTemp = alignement ;
                for ( unsigned int k = j+1 ; k < _hauteur ; k ++)
                {
                    if ( _plateau[i][k] == pion ) alignementMaxTemp++ ;
                    else k = _hauteur ;
                }
                if ( alignementMaxTemp > alignementMax ) alignementMax = alignementMaxTemp ;
                nbAlignements ++ ;
                compteur = 0 ;
            }
        }
    }

    /// Vérifications sur les diagonales ///
    int alignementMaxTemp = 0 ;
    nbAlignements += verifDiags(alignement, alignementMaxTemp, pion) ;
    if ( alignementMaxTemp > alignementMax ) alignementMax = alignementMaxTemp ;

    return nbAlignements ;
}



/**
  * @brief Vérification de la présence ou non du bon nombre de pions alignés sur les diagonales
  * @param alignement Le nombre de pions à aligner pour gagner
  * @param alignementMax Le nombre de pions alignés par le joueur au maximum
  * @param pion Le pion à vérifier, 'X' si c'est le joueur 1, 'O' sinon
  * @return Le nombre de fois que le pion est aligné le nombre de fois indiqué par alignement sur les diagonales
  */
int Plateau::verifDiags(int alignement, int & alignementMax, char pion)
{
    int nbAlignements = 0 ;
    int alignementMaxTemp ;
    for ( unsigned int i = 0 ; i < _largeur ; i++ )
    {
        alignementMaxTemp = 0 ;
        nbAlignements += verifDiagGauche(i,0,alignement,alignementMaxTemp,pion) ;
        if ( alignementMaxTemp > alignementMax ) alignementMax = alignementMaxTemp ;
        alignementMaxTemp = 0 ;
        nbAlignements += verifDiagDroite(i,0,alignement,alignementMaxTemp,pion) ;
        if ( alignementMaxTemp > alignementMax ) alignementMax = alignementMaxTemp ;
    }
    for ( unsigned int i = 1 ; i < _hauteur ; i++ )
    {
        alignementMaxTemp = 0 ;
        nbAlignements += verifDiagDroite(0,i,alignement,alignementMaxTemp,pion) ;
        if ( alignementMaxTemp > alignementMax ) alignementMax = alignementMaxTemp ;
        alignementMaxTemp = 0 ;
        nbAlignements += verifDiagGauche(_largeur-1,i,alignement,alignementMaxTemp,pion) ;
        if ( alignementMaxTemp > alignementMax ) alignementMax = alignementMaxTemp ;
    }
    return nbAlignements ;
}



/**
  * @brief Vérification de la présence ou non du bon nombre de pions alignés sur une diagonale orientée vers la droite
  * @param colonne La colonne du pion où commence la diagonale
  * @param ligne La ligne du pion où commence la diagonale
  * @param alignement Le nombre de pions à aligner pour gagner
  * @param alignementMax Le nombre de pions alignés par le joueur au maximum
  * @param pion Le pion à vérifier, 'X' si c'est le joueur 1, 'O' sinon
  * @return Le nombre de fois que le pion est aligné le nombre de fois indiqué par alignement sur la diagonale
  */
int Plateau::verifDiagDroite(unsigned int colonne, unsigned int ligne, int alignement, int & alignementMax, char pion)
{
    int nbAlignements = 0 ;
    int compteur = 0 ;
    while ( ligne < _hauteur && colonne < _largeur )
    {
        if ( _plateau[colonne][ligne] == pion )
        {
            compteur ++ ;
            if ( compteur > alignementMax ) alignementMax = compteur ;
        }
        else compteur = 0 ;
        if ( compteur == alignement )
        {
            int alignementMaxTemp = alignement ;
            unsigned int k = colonne+1 ;
            unsigned int l = ligne+1 ;
            while ( k < _largeur && l < _hauteur )
            {
                if ( _plateau[l][k] == pion ) alignementMaxTemp++ ;
                else l = _hauteur ;
                k++ ;
                l++ ;
            }
            if ( alignementMaxTemp > alignementMax ) alignementMax = alignementMaxTemp ;
            nbAlignements ++ ;
            compteur = 0 ;
        }
        ligne++ ;
        colonne++ ;
    }
    return nbAlignements ;
}



/**
  * @brief Vérification de la présence ou non du bon nombre de pions alignés sur une diagonale orientée vers la gauche
  * @param colonne La colonne du pion où commence la diagonale
  * @param ligne La ligne du pion où commence la diagonale
  * @param alignement Le nombre de pions à aligner pour gagner
  * @param alignementMax Le nombre de pions alignés par le joueur au maximum
  * @param pion Le pion à vérifier, 'X' si c'est le joueur 1, 'O' sinon
  * @return Le nombre de fois que le pion est aligné le nombre de fois indiqué par alignement sur la diagonale
  */
int Plateau::verifDiagGauche(unsigned int colonne, unsigned int ligne, int alignement, int & alignementMax, char pion)
{
    int nbAlignements = 0 ;
    int compteur = 0 ;
    while ( ligne < _hauteur && colonne < _largeur )
    {
        if ( _plateau[colonne][ligne] == pion )
        {
            compteur ++ ;
            if ( compteur > alignementMax ) alignementMax = compteur ;
        }
        else compteur = 0 ;
        if ( compteur == alignement )
        {
            int alignementMaxTemp = alignement ;
            unsigned int k = colonne-1 ;
            unsigned int l = ligne+1 ;
            while ( k < _largeur && l < _hauteur )
            {
                if ( _plateau[k][l] == pion ) alignementMaxTemp++ ;
                else l = _hauteur ;
                k-- ;
                l++ ;
            }
            if ( alignementMaxTemp > alignementMax ) alignementMax = alignementMaxTemp ;
            nbAlignements ++ ;
            compteur = 0 ;
        }
        ligne++ ;
        colonne-- ;
    }
    return nbAlignements ;
}



/**
  * @brief Permet d'attribuer un score à un état donné du plateau de jeu
  * @param alignement Le nombre de pions à aligner pour gagner la manche
  * @return Le score du plateau de jeu
  */
int Plateau::scoreAlignement(int alignement)
{
    if ( verif(alignement) == 'X' ) return -1000000 ;
    else if ( verif(alignement) == 'O' ) return 1000000 ;
    else
    {
        int alignementMaxJoueur1 = 0 ;
        int alignementMaxJoueur2 = 0 ;
        verif(alignement, alignementMaxJoueur1, 'X') ;
        verif(alignement, alignementMaxJoueur2, 'O') ;
        int scoreJ1 = 1 ;
        for ( int i = 0 ; i < alignementMaxJoueur1 ; i++ ) scoreJ1 *= 10 ;
        int scoreJ2 = 1 ;
        for ( int i = 0 ; i < alignementMaxJoueur2 ; i++ ) scoreJ2 *= 10 ;
        int diff = nbPions('X') - nbPions('O') ;
        return scoreJ2 - scoreJ1 - diff ;
    }
}



/**
  * @brief Compte le nombre de pions posés dans le plateau qui sont les mêmes que pion
  * @param pion Le pion à chercher
  * @return Le nombre de pions égaux à pion dans le plateau de jeu
  */
int Plateau::nbPions(char pion)
{
    int nb = 0 ;
    for ( unsigned int i = 0 ; i < _largeur ; i++ )
    {
        for ( unsigned int j = 0 ; j < _hauteur ; j++ )
        {
            if ( _plateau[i][j] == pion ) nb++ ;
        }
    }
    return nb ;
}



/**
  * @brief Vérification si le plateau de jeu est plein
  * @return True si le plateau est plein, false sinon
  */
bool Plateau::estPlein()
{
    for ( unsigned int i = 0 ; i < _largeur ; i++ )
    {
        if ( _plateau[i][0] == ' ' ) return false ;
    }
    return true ;
}



/**
  * @brief Vérification si une case est remplie ou non
  * @param colonne La colonne de la case à vérifier
  * @param ligne La ligne de la case à vérifier
  * @return True si la case est remplie, false sinon
  */
bool Plateau::casePleine(int colonne, int ligne)
{
    if ( _plateau[colonne][ligne] != ' ' ) return true ;
    else return false ;
}



/**
  * @brief Permet d'obtenir la ligne du pion le plus haut d'une colonne donnée, cette fonction est utilisée pour déterminer si une case est supprimable ou non
  * @param colonne La colonne à traiter
  * @return La ligne du pion le plus haut.
  */
int Plateau::pionLePlusHaut(int colonne)
{
    int i = 0 ;
    while ( _plateau[colonne][i] == ' ' ) i++ ;
    return i ;
}



/**
  * @brief Permet d'évaluer la valeur de chaque coup possible à un instant donné en fonction d'une profondeur
  * @param profondeur La profondeur du coup actuel
  * @param difficulteIa La profondeur maximale à atteindre, détermine la difficulté de l'ia
  * @param alignement le nombre de pions à aligner pour gagner la manche
  * @param pion Le pion du joueur actuel à la profondeur donnée
  * @return Le tableau des scores attribués à chaque action possible
  */
vector<int> Plateau::evaluation(int profondeur, int difficulteIa, int alignement, char pion)
{
    vector<int> eval ;
    for ( unsigned int i = 0 ; i < _largeur + _largeur * _hauteur + 2 ; i++ )
    {
        eval.push_back(0) ;
    }
    for ( unsigned int i = 0 ; i < _largeur + _largeur * _hauteur + 2 ; i++ )
    {
        if ( profondeur %2 == 0 ) pion = 'O' ;
        else pion = 'X' ;
        if ( i < _largeur && jouable(i) ) /// TESTS DES POSSIBILITES DE JOUER UN PION ///
        {
            jouer(i,pion) ;
            int tmp = _derniereColonneJouee ;
            _derniereColonneJouee = i ;
            profondeur ++ ;
            if ( scoreAlignement(alignement) == 1000000 || scoreAlignement(alignement) == -1000000 )
            {
                eval[i] = scoreAlignement(alignement) ;
            }
            else if ( estPlein() ) eval[i] = 1 ;
            else if ( profondeur == difficulteIa ) eval[i] = scoreAlignement(alignement) ;
            else if ( profondeur %2 == 0 ) eval[i] = max(evaluation(profondeur,difficulteIa,alignement,pion)) ;
            else eval[i] = min(evaluation(profondeur,difficulteIa,alignement,pion)) ;
            _derniereColonneJouee = tmp ;
            supprimer(i) ;
            profondeur -- ;
        }
        else if ( i >= _largeur && i < _largeur + _largeur * _hauteur ) /// TESTS DES POSSIBILITES DE SUPPRIMER UN PION ///
        {
            int colonne = ( ( i + _hauteur - _largeur ) / _hauteur ) - 1 ;  // Permet de transformer i en la colonne à tester
            int ligne = ( i + _hauteur - _largeur ) % _hauteur ;            // Permet de transformer i en la ligne à traiter
            if ( supprimable(colonne,ligne) )
            {
                std::vector< std::vector< char > > copie ;
                copie = _plateau ;
                supprimer(colonne,ligne) ;
                int tmp = _derniereColonneJouee ;
                _derniereColonneJouee = -1 ;
                profondeur ++ ;
                if ( scoreAlignement(alignement) == 1000000 || scoreAlignement(alignement) == -1000000 )
                {
                    eval[i] = scoreAlignement(alignement) ;
                }
                else if ( profondeur == difficulteIa ) eval[i] = scoreAlignement(alignement) ;
                else if ( profondeur %2 == 0 ) eval[i] = max(evaluation(profondeur,difficulteIa,alignement,pion)) ;
                else eval[i] = min(evaluation(profondeur,difficulteIa,alignement,pion)) ;
                _derniereColonneJouee = tmp ;
                _plateau = copie ;
                profondeur -- ;
            }
        }
        else if ( i == _largeur + _largeur * _hauteur ) /// TESTS DE LA POSSIBILITE DE TOURNER LE PLATEAU VERS LA DROITE ///
        {
            std::vector< std::vector< char > > copie ;
            copie = _plateau ;
            tournerDroite(true) ;
            int tmp = _derniereColonneJouee ;
            _derniereColonneJouee = -1 ;
            profondeur ++ ;
            if ( scoreAlignement(alignement) == 1000000 || scoreAlignement(alignement) == -1000000 )
            {
                eval[i] = scoreAlignement(alignement) ;
            }
            else if ( profondeur == difficulteIa ) eval[i] = scoreAlignement(alignement) ;
            else if ( profondeur %2 == 0 ) eval[i] = max(evaluation(profondeur,difficulteIa,alignement,pion)) ;
            else eval[i] = min(evaluation(profondeur,difficulteIa,alignement,pion)) ;
            int tmp2 = _largeur ;
            _largeur = _hauteur ;
            _hauteur = tmp2 ;
            _derniereColonneJouee = tmp ;
            _plateau = copie ;
            profondeur -- ;
        }
        else if ( i == _largeur + _largeur * _hauteur + 1 ) /// TESTS DE LA POSSIBILITE DE TOURNER LE PLATEAU VERS LA GAUCHE ///
        {
            std::vector< std::vector< char > > copie ;
            copie = _plateau ;
            tournerGauche(true) ;
            int tmp = _derniereColonneJouee ;
            _derniereColonneJouee = -1 ;
            profondeur ++ ;
            if ( scoreAlignement(alignement) == 1000000 || scoreAlignement(alignement) == -1000000 )
            {
                eval[i] = scoreAlignement(alignement) ;
            }
            else if ( profondeur == difficulteIa ) eval[i] = scoreAlignement(alignement) ;
            else if ( profondeur %2 == 0 ) eval[i] = max(evaluation(profondeur,difficulteIa,alignement,pion)) ;
            else eval[i] = min(evaluation(profondeur,difficulteIa,alignement,pion)) ;
            int tmp2 = _largeur ;
            _largeur = _hauteur ;
            _hauteur = tmp2 ;
            _derniereColonneJouee = tmp ;
            _plateau = copie ;
            profondeur -- ;
        }
    }
    return eval ;
}



/// ESSAIS AVEC ALPHA BETA ///
/*vector<int> Plateau::evaluation2(int profondeur, int difficulteIa, int alignement, int & alpha, int & beta, char pion)
{
    vector<int> eval ;
    for ( unsigned int i = 0 ; i < _largeur + _largeur * _hauteur + 2 ; i++ )
    {
        eval.push_back(0) ;
    }
    for ( unsigned int i = 0 ; i < _largeur + _largeur * _hauteur + 2 ; i++ )
    {
        if ( profondeur %2 == 0 )
        {
            if ( eval[i] != 0 && eval[i-1] > beta )
            {
                for ( unsigned int i = 0 ; i < eval.size() ; i++ ) eval[i] = alpha ;
                return eval ;
            }
            pion = 'O' ;
        }
        else
        {
            if ( eval[i] != 0 && eval[i-1] < alpha )
            {
                for ( unsigned int i = 0 ; i < eval.size() ; i++ ) eval[i] = beta ;
                return eval ;
            }
            pion = 'X' ;
        }
        if ( i < _largeur && jouable(i) )
        {
            jouer(i,pion) ;
            int tmp = _derniereColonneJouee ;
            _derniereColonneJouee = i ;
            profondeur ++ ;
            if ( scoreAlignement(alignement) == 1000000 || scoreAlignement(alignement) == -1000000 )
            {
                eval[i] = scoreAlignement(alignement) ;
            }
            else if ( estPlein() ) eval[i] = 1 ;
            else if ( profondeur == difficulteIa )
            {
                eval[i] = scoreAlignement(alignement) ;
                if ( profondeur %2 == 0 && eval[i] > alpha ) alpha = eval[i] ;
                else if ( profondeur %2 != 0 && eval[i] < beta ) beta = eval[i] ;
            }
            else if ( profondeur %2 == 0 ) eval[i] = max(evaluation2(profondeur,difficulteIa,alignement,alpha,beta,pion)) ;
            else eval[i] = min(evaluation2(profondeur,difficulteIa,alignement,alpha,beta,pion)) ;
            _derniereColonneJouee = tmp ;
            supprimer(i) ;
            profondeur -- ;
        }
        else if ( i >= _largeur && i < _largeur + _largeur * _hauteur )
        {
            int colonne = ( ( i + _hauteur - _largeur ) / _hauteur ) - 1 ;
            int ligne = ( i + _hauteur - _largeur ) % _hauteur ;
            if ( supprimable(colonne,ligne) )
            {
                std::vector< std::vector< char > > copie ;
                copie = _plateau ;
                supprimer(colonne,ligne) ;
                int tmp = _derniereColonneJouee ;
                _derniereColonneJouee = -1 ;
                profondeur ++ ;
                if ( scoreAlignement(alignement) == 1000000 || scoreAlignement(alignement) == -1000000 )
                {
                    eval[i] = scoreAlignement(alignement) ;
                }
                else if ( profondeur == difficulteIa )
                {
                    eval[i] = scoreAlignement(alignement) ;
                    if ( profondeur %2 == 0 && eval[i] > alpha ) alpha = eval[i] ;
                    else if ( profondeur %2 != 0 && eval[i] < beta ) beta = eval[i] ;
                }
                else if ( profondeur %2 == 0 ) eval[i] = max(evaluation(profondeur,difficulteIa,alignement,pion)) ;
                else eval[i] = min(evaluation(profondeur,difficulteIa,alignement,pion)) ;
                _derniereColonneJouee = tmp ;
                _plateau = copie ;
                profondeur -- ;
            }
        }
        else if ( i == _largeur + _largeur * _hauteur )
        {
            std::vector< std::vector< char > > copie ;
            copie = _plateau ;
            tournerDroite(true) ;
            int tmp = _derniereColonneJouee ;
            _derniereColonneJouee = -1 ;
            profondeur ++ ;
            if ( scoreAlignement(alignement) == 1000000 || scoreAlignement(alignement) == -1000000 )
            {
                eval[i] = scoreAlignement(alignement) ;
            }
            else if ( profondeur == difficulteIa )
            {
                eval[i] = scoreAlignement(alignement) ;
                if ( profondeur %2 == 0 && eval[i] > alpha ) alpha = eval[i] ;
                else if ( profondeur %2 != 0 && eval[i] < beta ) beta = eval[i] ;
            }
            else if ( profondeur %2 == 0 ) eval[i] = max(evaluation(profondeur,difficulteIa,alignement,pion)) ;
            else eval[i] = min(evaluation(profondeur,difficulteIa,alignement,pion)) ;
            int tmp2 = _largeur ;
            _largeur = _hauteur ;
            _hauteur = tmp2 ;
            _derniereColonneJouee = tmp ;
            _plateau = copie ;
            profondeur -- ;
        }
        else if ( i == _largeur + _largeur * _hauteur + 1 )
        {
            std::vector< std::vector< char > > copie ;
            copie = _plateau ;
            tournerGauche(true) ;
            int tmp = _derniereColonneJouee ;
            _derniereColonneJouee = -1 ;
            profondeur ++ ;
            if ( scoreAlignement(alignement) == 1000000 || scoreAlignement(alignement) == -1000000 )
            {
                eval[i] = scoreAlignement(alignement) ;
            }
            else if ( profondeur == difficulteIa )
            {
                eval[i] = scoreAlignement(alignement) ;
                if ( profondeur %2 == 0 && eval[i] > alpha ) alpha = eval[i] ;
                else if ( profondeur %2 != 0 && eval[i] < beta ) beta = eval[i] ;
            }
            else if ( profondeur %2 == 0 ) eval[i] = max(evaluation(profondeur,difficulteIa,alignement,pion)) ;
            else eval[i] = min(evaluation(profondeur,difficulteIa,alignement,pion)) ;
            int tmp2 = _largeur ;
            _largeur = _hauteur ;
            _hauteur = tmp2 ;
            _derniereColonneJouee = tmp ;
            _plateau = copie ;
            profondeur -- ;
        }
    }
    return eval ;
}*/



/**
  * @brief Permet d'obtenir la valeur minimum d'un tableau donné, utilisé par l'ia pour déterminer le moins bon coup possible
  * @param eval Le tableau à traiter
  * @return La plus petite valeur trouvée dans le tableau qui correspond à un coup autorisé
  */
int Plateau::min(vector<int> eval)
{
    int min = 1000000 ;
    for ( unsigned int i = 0 ; i < eval.size() ; i++ )
    {
        if ( min > eval[i])
        {
            int colonne = ( ( i + _hauteur - _largeur ) / _hauteur ) - 1 ;
            int ligne = ( i + _hauteur - _largeur ) % _hauteur ;
            if ( i >= _largeur + _largeur * _hauteur || ( i >= _largeur && i < _largeur + _largeur * _hauteur && supprimable(colonne,ligne) ) || ( i < _largeur && jouable(i) ) ) min = eval[i] ;
        }
    }
    return min ;
}


/**
  * @brief Permet d'obtenir la valeur maximum d'un tableau donné, utilisé par l'ia pour déterminer le meilleur coup possible
  * @param eval Le tableau à traiter
  * @return La plus grande valeur trouvée dans le tableau qui correspond à un coup autorisé
  */
int Plateau::max(vector<int> eval)
{
    int max = -1000000 ;
    for ( unsigned int i = 0 ; i < eval.size() ; i++ )
    {
        if ( max < eval[i])
        {
            int colonne = ( ( i + _hauteur - _largeur ) / _hauteur ) - 1 ;
            int ligne = ( i + _hauteur - _largeur ) % _hauteur ;
            if ( i >= _largeur + _largeur * _hauteur || ( i >= _largeur && i < _largeur + _largeur * _hauteur && supprimable(colonne,ligne) ) || ( i < _largeur && jouable(i) ) ) max = eval[i] ;
        }
    }
    return max ;
}



/**
  * @brief Permet de choisir une valeur aléatoire parmis tous les maximum d'une évaluation donnée
  * @param eval Le tableau à traité obtenu avec l'évaluation
  * @return L'indice du coup à jouer
  */
int Plateau::maxEval(vector<int> eval)
{
    int max = eval[0] ;
    vector<int> tmp ;
    for ( unsigned int i = 0 ; i < eval.size() ; i++ )
    {
        if ( max < eval[i] )
        {
            int colonne = ( ( i + _hauteur - _largeur ) / _hauteur ) - 1 ;
            int ligne = ( i + _hauteur - _largeur ) % _hauteur ;
            if ( i >= _largeur + _largeur * _hauteur || ( i >= _largeur && i < _largeur + _largeur * _hauteur && supprimable(colonne,ligne) ) || ( i < _largeur && jouable(i) ) ) max = eval[i] ;
        }
    }
    for ( unsigned int i = 0 ; i < eval.size() ; i++ )
    {
        if ( max == eval[i] )
        {
            int colonne = ( ( i + _hauteur - _largeur ) / _hauteur ) - 1 ;
            int ligne = ( i + _hauteur - _largeur ) % _hauteur ;
            if ( i >= _largeur + _largeur * _hauteur || ( i >= _largeur && i < _largeur + _largeur * _hauteur && supprimable(colonne,ligne) ) || ( i < _largeur && jouable(i) ) ) tmp.push_back(i) ;
        }
    }
    int choix = randVect(tmp) ;
    return choix ;
}



/**
  * @brief Permet d'obtenir une valeur aléatoire parmis celles d'un tableau donné
  * @param eval Le tableau à utiliser
  * @return La valeur choisie aléatoirement
  */
int Plateau::randVect(vector<int> eval)
{
    srand (time(NULL));
    return eval[rand() % eval.size()] ;
}



/**
  * @brief Permet à l'ordinateur de faire un coup en fonction de l'évaluation donnée
  * @param eval Le tableau obtenu à l'aide de l'évaluation
  * @param difficulteIa La difficulté de l'ia = la profondeur
  * @param alignement Le nombre de pions à aligner pour gagner la manche
  */
void Plateau::jouerIa(vector<int> eval, int difficulteIa, int alignement)
{
    unsigned int choix = maxEval(eval) ;
    while ( ( max(eval) == -1000000 ) && difficulteIa > 2 ) // Au cas où l'ia sait qu'elle va perdre, pour qu'elle éssaie de perdre le plus tard possible
    {
        difficulteIa -= 2 ;
        eval = evaluation(0,difficulteIa,alignement) ;
        choix = maxEval(eval) ;
    }
    if ( choix < _largeur ) // L'ia doit jouer un pion
    {
        cout << endl << "L'ordinateur a joue dans la colonne "<< choix+1 << "." << endl << endl ;
        jouer(choix,'O') ;
        _derniereColonneJouee = choix ;
    }
    else if ( choix == _largeur + _largeur * _hauteur ) // L'ia doit supprimer un pion
    {
        cout << endl << "L'ordinateur a tourne le plateau vers la droite." << endl << endl ;
        tournerDroite(true) ;
        _derniereColonneJouee = -1 ;
    }
    else if ( choix == _largeur + _largeur * _hauteur + 1 ) // L'ia doit tourner le plateau vers la droite
    {
        cout << endl << "L'ordinateur a tourne le plateau vers la gauche." << endl << endl ;
        tournerGauche(true) ;
        _derniereColonneJouee = -1 ;
    }
    else // L'ia doit tourner le plateau vers la gauche
    {
        supprimerIa(choix) ;
        _derniereColonneJouee = -1 ;
    }
}



/**
  * @brief Suppression d'un pion par l'ordinateur
  * @param choix L'indice permettant de savoir quel pion doit être supprimé, à transformer en colonne et ligne pour être utilisable
  */
void Plateau::supprimerIa(int choix)
{
    int colonne = ( ( choix + _hauteur - _largeur ) / _hauteur ) - 1 ;
    int ligne = ( choix + _hauteur - _largeur ) % _hauteur ;
    cout << endl << "L'ordinateur a supprime le pion en (" << colonne+1 << "," << ligne+1 << ")." << endl << endl ;
    supprimer(colonne,ligne) ;
}



/**
  * @brief Sauvegarde l'état du plateau de jeu dans le fichier save
  */
void Plateau::sauvegarder()
{
    ofstream file("save", ios::out | ios::app) ;
    if ( !file )
    {
        cout << "Le fichier n'a pas pu etre sauvegarde ..." << endl ;
    }
    else
    {
        file << _largeur << " " << _hauteur << endl ;
        for ( unsigned int i = 0 ; i < _hauteur ; i++ )
        {
            for ( unsigned int j = 0 ; j < _largeur ; j++ )
            {
                file << _plateau[j][i] ;
            }
            file << endl ;
        }
    }
    file.close() ;
}
